package com.example.score.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.score.dto.AjaxResult;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import okhttp3.*;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.net.ssl.*;
import java.io.*;
import java.net.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 通用http发送方法
 *
 * @author tms
 */
public class HttpUtils {
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
    private static int TIME_OUT = 15;


    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param) {
        return sendGet(url, param, Constants.UTF8);
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url         发送请求的 URL
     * @param param       请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String contentType) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}", result);
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            String urlNameString = url;
            log.info("sendPost - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}", result);
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    public static String sendSSLPost(String url, String param) {
        StringBuilder result = new StringBuilder();
        String urlNameString = url + "?" + param;
        try {
            log.info("sendSSLPost - {}", urlNameString);
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());
            URL console = new URL(urlNameString);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);

            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null) {
                if (ret != null && !"".equals(ret.trim())) {
                    result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
                }
            }
            log.info("recv - {}", result);
            conn.disconnect();
            br.close();
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }

    private static class TrustAnyTrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[]{};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    public static String postWithJson(String url, Object obj) {
        if (obj == null) {
            obj = new Object();
        }

        Gson gson = new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .create();

        log.info(" url:{},para;{}", url, gson.toJson(obj));
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), gson.toJson(obj));
        OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(300, TimeUnit.SECONDS).build();
        final Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        Call call = okHttpClient.newCall(request);
        try (Response response = call.execute()) {
            String callResult = response.body().string();
            log.info("url:{},para:{},response:{}", url, gson.toJson(obj), callResult);
            return callResult;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String postWithJson(String url, Object obj, String auth) {
        if (obj == null) {
            obj = new Object();
        }

        Gson gson = new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .create();

        log.info(" url:{},para;{}", url, gson.toJson(obj));
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), gson.toJson(obj));
        OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(300, TimeUnit.SECONDS).build();
        final Request request = new Request.Builder()
                .header("Authorization", auth)
                .url(url)
                .post(requestBody)
                .build();
        Call call = okHttpClient.newCall(request);
        try (Response response = call.execute()) {
            String callResult = response.body().string();
            log.info("url:{},para:{},response:{}", url, gson.toJson(obj), callResult);
            return callResult;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String putWithJson(String url, Object obj) {
        if (obj == null) {
            obj = new Object();
        }

        Gson gson = new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .create();

        log.info(" url:{},para;{}", url, gson.toJson(obj));
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), gson.toJson(obj));
        OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(300, TimeUnit.SECONDS).build();
        final Request request = new Request.Builder()
                .url(url)
                .put(requestBody)
                .build();
        Call call = okHttpClient.newCall(request);
        try (Response response = call.execute()) {
            String callResult = response.body().string();
            log.info("url:{},para:{},response:{}", url, gson.toJson(obj), callResult);
            return callResult;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String putWithJson(String url, Object obj, String auth) {
        if (obj == null) {
            obj = new Object();
        }

        Gson gson = new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .create();

        log.info(" url:{},para;{}", url, gson.toJson(obj));
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), gson.toJson(obj));
        OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(300, TimeUnit.SECONDS).build();
        final Request request = new Request.Builder()
                .header("Authorization", auth)
                .url(url)
                .put(requestBody)
                .build();
        Call call = okHttpClient.newCall(request);
        try (Response response = call.execute()) {
            String callResult = response.body().string();
            log.info("url:{},para:{},response:{}", url, gson.toJson(obj), callResult);
            return callResult;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String get(String url, Map<String, String> para) throws IOException {
        if (para == null) {
            para = new HashMap();
        }
        log.info(" url:{},para;{}", url, JSON.toJSONString(para));
        StringBuilder finalUrl = new StringBuilder(url);
        if (MapUtils.isNotEmpty(para)) {
            finalUrl.append("?");
            Iterator<Map.Entry<String, String>> iterator = para.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> element = iterator.next();
                finalUrl.append(element.getKey());
                finalUrl.append("=");
                finalUrl.append(element.getValue());
                finalUrl.append("&");
            }
        }
        OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(TIME_OUT, TimeUnit.SECONDS).build();
        final Request request = new Request.Builder()
                .url(finalUrl.toString())
                .get()
                .build();
        Call call = okHttpClient.newCall(request);
        try (Response response = call.execute()) {
            String result = response.body().string();
            log.info("url:{},para:{},response:{}", url, JSON.toJSONString(para), result);
            return result;
        } catch (IOException e) {
            throw e;
        }
    }

    public static String get(String url, Map<String, String> para, String auth) throws IOException {
        if (para == null) {
            para = new HashMap();
        }
        log.info(" url:{},para;{}", url, JSON.toJSONString(para));
        StringBuilder finalUrl = new StringBuilder(url);
        if (MapUtils.isNotEmpty(para)) {
            finalUrl.append("?");
            Iterator<Map.Entry<String, String>> iterator = para.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> element = iterator.next();
                finalUrl.append(element.getKey());
                finalUrl.append("=");
                finalUrl.append(element.getValue());
                finalUrl.append("&");
            }
        }
        OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(TIME_OUT, TimeUnit.SECONDS).build();
        final Request request = new Request.Builder()
                .header("Authorization", auth)
                .url(finalUrl.toString())
                .get()
                .build();
        Call call = okHttpClient.newCall(request);
        try (Response response = call.execute()) {
            String result = response.body().string();
            log.info("url:{},para:{},response:{}", url, JSON.toJSONString(para), result);
            return result;
        } catch (IOException e) {
            throw e;
        }
    }


    /*
     * 表单上传
     */
    public static String sendForm(String url, Map<String, String> fields, Map<String, String> files) {
        DataOutputStream out = null;
        BufferedReader in = null;
        StringBuffer sb = null;
        String result = "";

        String BOUNDARY = "----123852369147ABC";

        try {
            URL realUrl = new URL(url);

            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();


            connection.setRequestProperty("tag", getTag(connection));

            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Charset", "UTF-8");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0");
            connection.setRequestProperty("Connection", "Keep-Alive");
            connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);

            out = new DataOutputStream(connection.getOutputStream());

            if (fields != null) {
                Iterator<Map.Entry<String, String>> iter = fields.entrySet().iterator();

                sb = new StringBuffer();
                while (iter.hasNext()) {
                    Map.Entry<String, String> entry = iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue = (String) entry.getValue();
                    if (inputValue == null) {
                        continue;
                    }
                    sb.append("\r\n").append("--").append(BOUNDARY).append("\r\n");
                    sb.append("Content-Disposition: form-data; name=\"" + inputName + "\"\r\n\r\n");
                    sb.append(inputValue);
                }
                out.write(sb.toString().getBytes("UTF-8"));
            }

            if (files != null) {
                Iterator<Map.Entry<String, String>> iter = files.entrySet().iterator();

                while (iter.hasNext()) {
                    Map.Entry<String, String> entry = iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue = (String) entry.getValue();
                    if (inputValue == null) {
                        continue;
                    }
                    File file = new File(inputValue);
                    String fileName = file.getName();

                    //String contentType = GetContentType(fileName);
                    String contentType = "multipart/form-data;";
                    sb = new StringBuffer();
                    sb.append("\r\n").append("--").append(BOUNDARY).append("\r\n");
                    sb.append("Content-Disposition: form-data; name=\"" + inputName + "\"; filename=\"" + fileName
                            + "\"\r\n");
                    sb.append("Content-Type:" + contentType + "\r\n\r\n");

                    out.write(sb.toString().getBytes("UTF-8"));

                    DataInputStream dsFile = new DataInputStream(new FileInputStream(file));
                    byte[] bytes = new byte[1024];
                    int count = 0;
                    while ((count = dsFile.read(bytes)) != -1) {
                        out.write(bytes, 0, count);
                    }
                    dsFile.close();
                }
            }

            byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");
            out.write(endData);
            out.flush();
            out.close();

            int code = connection.getResponseCode();
            if (code == HttpURLConnection.HTTP_OK) {
                in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF8"));
            } else {
                in = new BufferedReader(new InputStreamReader(connection.getErrorStream(), "UTF8"));
            }

            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }

            if (code != HttpURLConnection.HTTP_OK) {
                throw new Exception(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

    private static String getTag(URLConnection connection) {
        JSONObject jo = new JSONObject();

        Map<String, List<String>> map = connection.getRequestProperties();

        for (String key : map.keySet()) {
            if (key != null && key.length() > 0) {
                jo.put(key, map.get(key));
            }
        }

        // 当前用户IP
        jo.put("IP", getUserIP());

        return jo.toString();
    }

    private static String getUserIP() {
        // 请修改为当前用户IP
        return "";
    }

    /*
     * 获取文件的ContentType
     */
    private static String GetContentType(String file) {
        try {
            Path path = Paths.get(file);

            return Files.probeContentType(path);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static String uploadFile(String url, MultipartFile file) throws IOException {
        // 创建 OkHttpClient 实例
        OkHttpClient client = new OkHttpClient();

        // 将 MultipartFile 转换为 File
        File tempFile = convertMultipartFileToFile(file);

        // 创建 RequestBody 用于文件上传
        RequestBody fileBody = RequestBody.create(MediaType.get("application/octet-stream"), tempFile);

        // 创建 MultipartBody 请求体
        MultipartBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", tempFile.getName(), fileBody)  // "file" 是表单中接收文件的字段名
                .build();

        // 创建 POST 请求
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();

        // 执行请求
        try (Response response = client.newCall(request).execute()) {
            // 输出响应内容
            if (response.isSuccessful()) {
                return "文件上传成功";
            } else {
                return "文件上传失败，响应码：" + response.code();
            }
        }
    }

    // 辅助方法：将 MultipartFile 转换为 File
    private static File convertMultipartFileToFile(MultipartFile file) throws IOException {
        File tempFile = new File(System.getProperty("java.io.tmpdir"), file.getOriginalFilename());
        file.transferTo(tempFile);  // 将 MultipartFile 转换为 File
        return tempFile;
    }

    public static byte[] getByteResponse(String url, Map<String, String> para) throws IOException {
        if (para == null) {
            para = new HashMap();
        }
        log.info(" url:{},para;{}", url, JSON.toJSONString(para));
        StringBuilder finalUrl = new StringBuilder(url);
        if (MapUtils.isNotEmpty(para)) {
            finalUrl.append("?");
            Iterator<Map.Entry<String, String>> iterator = para.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> element = iterator.next();
                finalUrl.append(element.getKey());
                finalUrl.append("=");
                finalUrl.append(element.getValue());
                finalUrl.append("&");
            }
        }
        OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(TIME_OUT, TimeUnit.SECONDS).build();
        final Request request = new Request.Builder()
                .url(finalUrl.toString())
                .get()
                .build();
        Call call = okHttpClient.newCall(request);
        try (Response response = call.execute()) {
//            String result = response.body().string();
//            log.info("url:{},para:{},response:{}", url, JSON.toJSONString(para), result);
//            return result;
            return response.body().bytes();
        } catch (IOException e) {
            throw e;
        }
    }
}